package cn.afei.cloud.config.redis;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 缓存操作类
 */
@Component
public class CacheUtils {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private RedisTemplate<String, Object> redisObjectTemplate;


    // 维护一个本类的静态变量
    private static CacheUtils cacheUtils;


    @PostConstruct
    public void init() {
        cacheUtils = this;
        cacheUtils.redisTemplate = this.redisTemplate;
        cacheUtils.redisObjectTemplate = this.redisObjectTemplate;
    }

    /**
     * 使用lur脚本删除key
     * @param key
     * @param value
     * @throws Exception
     */
    public static void delLurKey(String key,String value) throws Exception {
        Jedis jedis = RedisUtils.getJedis();
        // lua 脚本，摘自官网
        String script = "if redis.call('get', KEYS[1]) == ARGV[1]" + "then "
                + "return redis.call('del', KEYS[1])" + "else " + "  return 0 " + "end";
        try {
            // 执行 lua 脚本
            Object result = jedis.eval(script, Collections.singletonList(key), Collections.singletonList(value));
            // 获取 lua 脚本的执行结果
            if ("1".equals(result.toString())) {
                System.out.println("------del REDIS_LOCK_KEY success");
            } else {
                System.out.println("------del REDIS_LOCK_KEY error");
            }
        } finally {
            // 关闭链接
            if (null != jedis) {
                jedis.close();
            }
        }

    }

    /**
     * 事务删除key保持原子性操作
     *
     * @param key
     */
    public static void deleteTransKey(String key, String value) {
        while (true) {
            cacheUtils.redisTemplate.watch(key);
            if (CacheUtils.hasKey(RedisConstant.REDIS_LOCK)) {
                if (StringUtils.isNotBlank(value) && CacheUtils.get(RedisConstant.REDIS_LOCK).equalsIgnoreCase(value)) {
                    cacheUtils.redisTemplate.setEnableTransactionSupport(true);
                    cacheUtils.redisTemplate.multi();
                    cacheUtils.redisTemplate.delete(key);
                    List<Object> list = cacheUtils.redisTemplate.exec();
                    if (list == null) {
                        continue;
                    }
                } else {
                    cacheUtils.redisTemplate.setEnableTransactionSupport(true);
                    cacheUtils.redisTemplate.multi();
                    cacheUtils.redisTemplate.delete(key);
                    List<Object> list = cacheUtils.redisTemplate.exec();
                    if (list == null) {
                        continue;
                    }
                }
                cacheUtils.redisTemplate.unwatch();
                break;
            }
        }
    }

    public static Long getCounter(String key) {
        RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, cacheUtils.redisObjectTemplate.getConnectionFactory());
        Long increment = entityIdCounter.getAndIncrement();
        return increment;
    }

    /**
     * 将参数中的字符串值设置为键的值，不设置过期时间
     *
     * @param key
     * @param value 必须要实现 Serializable 接口
     */
    public static void set(String key, String value) {
        cacheUtils.redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 将参数中的字符串值设置为键的值，不设置过期时间
     *
     * @param key
     * @param value 必须要实现 Serializable 接口
     */
    public static void setObject(String key, Object value) {
        cacheUtils.redisObjectTemplate.opsForValue().set(key, value);
    }

    /**
     * 将参数中的字符串值设置为键的值，设置过期时间
     *
     * @param key
     * @param value   必须要实现 Serializable 接口
     * @param timeout
     */
    public static void set(String key, String value, Long timeout) {
        cacheUtils.redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }

    /**
     * 获取与指定键相关的值
     *
     * @param key
     * @return
     */
//    public static Object get(String key) {
//        return cacheUtils.redisTemplate.opsForValue().get(key);
//    }
    public static String get(String key) {
        return cacheUtils.redisTemplate.opsForValue().get(key);
    }

    public static Object getObject(String key) {
        return cacheUtils.redisObjectTemplate.opsForValue().get(key);
    }

    /**
     * 设置某个键的过期时间
     *
     * @param key 键值
     * @param ttl 过期秒数
     */
    public static boolean expire(String key, Long ttl) {
        return cacheUtils.redisTemplate.expire(key, ttl, TimeUnit.SECONDS);
    }

    /**
     * 判断某个键是否存在
     *
     * @param key 键值
     */
    public static boolean hasKey(String key) {
        return cacheUtils.redisTemplate.hasKey(key);
    }

    /**
     * 向集合添加元素
     *
     * @param key
     * @param value
     * @return 返回值为设置成功的value数
     */
    public static Long sAdd(String key, String... value) {
        return cacheUtils.redisTemplate.opsForSet().add(key, value);
    }

    /**
     * 获取集合中的某个元素
     *
     * @param key
     * @return 返回值为redis中键值为key的value的Set集合
     */
    public static Set<String> sGetMembers(String key) {
        return cacheUtils.redisTemplate.opsForSet().members(key);
    }

    /**
     * 获取集合中的某个元素
     *
     * @param key
     * @return 返回值为redis中键值为key的value的Set集合
     */
    public static void sRemoveMembers(String key, String value) {
        cacheUtils.redisTemplate.opsForSet().remove(key, value);
    }

    /**
     * 将给定分数的指定成员添加到键中存储的排序集合中
     *
     * @param key
     * @param value
     * @param score
     * @return
     */
    public static Boolean zAdd(String key, String value, double score) {
        return cacheUtils.redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 返回指定排序集中给定成员的分数
     *
     * @param key
     * @param value
     * @return
     */
    public static Double zScore(String key, String value) {
        return cacheUtils.redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 删除指定的键
     *
     * @param key
     * @return
     */
    public static Boolean delete(String key) {
        return cacheUtils.redisTemplate.delete(key);
    }

    /**
     * 删除多个键
     *
     * @param keys
     * @return
     */
    public static Long delete(Collection<String> keys) {
        return cacheUtils.redisTemplate.delete(keys);
    }

    /**
     * 向集合添加元素
     *
     * @param key
     * @param value
     * @return 返回值为设置成功的value数
     */
    public static Object getByValue(String key, String value) {
        return cacheUtils.redisTemplate.opsForHash().get(key, value);
    }

    /**
     *      * 获取锁，true 则得到锁，false 已被锁定
     *      * 
     *      * @param lockName   锁名称
     *      * @param lockExoire 锁时间
     *      * @return
     *     
     */
    public static Boolean getLock(String lockName, Integer lockExoire) {
        return (Boolean) cacheUtils.redisTemplate.execute((RedisCallback<?>) connection -> {
            // 获取时间毫秒值
            long expireAt = System.currentTimeMillis() + lockExoire + 1;
            // 获取锁
            Boolean acquire = connection.setNX(lockName.getBytes(), String.valueOf(expireAt).getBytes());
            if (acquire) {
                return true;
            } else {
                byte[] bytes = connection.get(lockName.getBytes());
                // 非空判断
                if (Objects.nonNull(bytes) && bytes.length > 0) {
                    long expireTime = Long.parseLong(new String(bytes));
                    // 如果锁已经过期
                    if (expireTime < System.currentTimeMillis()) {
                        // 重新加锁，防止死锁
                        byte[] set = connection.getSet(lockName.getBytes(),
                                String.valueOf(System.currentTimeMillis() + lockExoire + 1).getBytes());
                        return Long.parseLong(new String(set)) < System.currentTimeMillis();
                    }
                }
            }
            return false;
        });
    }

    /**
     * 判断是否包含  sismember
     *
     * @param key
     * @param value
     */
    public static Boolean contains(String key, String value) {
        return cacheUtils.redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 获取集合中所有的值 smembers
     *
     * @param key
     * @return
     */
    public static Set<String> values(String key) {
        return cacheUtils.redisTemplate.opsForSet().members(key);
    }

    /**
     * 返回多个集合的并集  sunion
     *
     * @param key1
     * @param key2
     * @return
     */
    public static Set<String> union(String key1, String key2) {
        return cacheUtils.redisTemplate.opsForSet().union(key1, key2);
    }

    /**
     * 返回多个集合的交集 sinter
     *
     * @param key1
     * @param key2
     * @return
     */
    public static Set<String> intersect(String key1, String key2) {
        return cacheUtils.redisTemplate.opsForSet().intersect(key1, key2);
    }

    /**
     * 返回集合key1中存在，但是key2中不存在的数据集合  sdiff
     *
     * @param key1
     * @param key2
     * @return
     */
    public static Set<String> diff(String key1, String key2) {
        return cacheUtils.redisTemplate.opsForSet().difference(key1, key2);
    }

    public static Long leftPush(String key, String value) {
        return cacheUtils.redisTemplate.opsForList().leftPush(key, value);
    }

    public static Long leftPushAll(String key, List<String> value) {
        return cacheUtils.redisTemplate.opsForList().leftPushAll(key, value);
    }

    public static String leftPop(String key) {
        return cacheUtils.redisTemplate.opsForList().leftPop(key);
    }

    public static List<String> range(String key) {
        return cacheUtils.redisTemplate.opsForList().range(key, 0, -1);
    }

    public static Long listRemove(String key, String value) {
        return cacheUtils.redisTemplate.opsForList().remove(key, 0, value);
    }

    public static Long listObjectRemove(String key, Object value) {
        return cacheUtils.redisObjectTemplate.opsForList().remove(key, 0, value);
    }

    public static Long leftPushObjectAll(String key, List value) {
        return cacheUtils.redisObjectTemplate.opsForList().leftPushAll(key, value);
    }

    public static List rangeObject(String key, long start, long end) {
        return cacheUtils.redisObjectTemplate.opsForList().range(key, start, end);
    }


    public static List rangeObject(String key) {
        return cacheUtils.redisObjectTemplate.opsForList().range(key, 0, -1);
    }

    public static Long leftPushObject(String key, Object value) {
        return cacheUtils.redisObjectTemplate.opsForList().leftPush(key, value);
    }

    public static Long listRemoveCountObject(String key, Long count, Object value) {
        return cacheUtils.redisObjectTemplate.opsForList().remove(key, count, value);
    }

    public static Long chongfutijiao(String key) {
        return cacheUtils.redisTemplate.opsForValue().increment(key, 1);
    }

    public static boolean chongfutijiaoUnlock(String key) {
        return cacheUtils.redisTemplate.expire(key, 60, TimeUnit.SECONDS);
    }

    public static boolean chongfutijiaoUnlockTimeout(String key, Long timeout) {
        return cacheUtils.redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 删除指定的键
     *
     * @param key
     * @return
     */
    public static Long deleteBatchKey(String key) {
        Set keys = cacheUtils.redisTemplate.keys(key);
        return cacheUtils.redisTemplate.delete(keys);
    }

}
